#include"UdpSocket.h"

using namespace tmms::network;


UdpSocket::UdpSocket(EventLoop * loop,
                        int socketfd,
                        const InetAddress &localAddr,
                        const InetAddress &peerAddr)
:Connection(loop,socketfd,localAddr,peerAddr),message_buffer_(message_buffer_size)
{

}

UdpSocket::~UdpSocket()
{

}



void UdpSocket::OnTimeOut()
{
    NEWORK_ERROR<<"host:" <<peer_addr_.ToIpPort()<<"timeout close it.";
    OnClose();
}

void UdpSocket::OnError(const std::string &msg) 
{
    NEWORK_ERROR<<"host:" <<peer_addr_.ToIpPort()<<"error:" << msg;
    OnClose();
}

void UdpSocket::OnRead() 
{
   if(closed_)
   {
        NEWORK_TRACE<<"host:"<<peer_addr_.ToIpPort()<<"had closed.";
        return ;
   };
   ExtenLife();
   while (true)
   {
        struct sockaddr_in6 sock_addr;
        socklen_t len = sizeof(sock_addr);
    

        auto ret =::recvfrom(fd_,message_buffer_.beginWrite(),
                            message_buffer_size,
                            0,
                            (struct sockaddr *)&sock_addr,
                            &len);

        if(ret > 0)
        {
            InetAddress peeraddr ;
            message_buffer_.hasWritten(ret);
            if(sock_addr.sin6_family ==AF_INET)//IPv4 网络协议
            {
                char ip[INET_ADDRSTRLEN] = {0,};
                struct  sockaddr_in * saddr =(struct sockaddr_in *)&sock_addr;//将通用套接字地址结构 addr 强制转换为 IPv4 专用结构 sockaddr_in 的指针​​
                ::inet_ntop(AF_INET,&(saddr->sin_addr.s_addr),ip,sizeof(ip));//此函数用于​​将二进制 IPv6 地址转换为可读字符串​​
                peeraddr.SetAddr(ip);
                peeraddr.SetPort(ntohs(saddr->sin_port));
            }
            else if(sock_addr.sin6_family ==AF_INET6){ //IPv6 网络协议
                char ip[INET6_ADDRSTRLEN] = {0,};
                struct  sockaddr_in * saddr =(struct sockaddr_in *)&sock_addr;
                ::inet_ntop(AF_INET6,&(sock_addr.sin6_addr),ip,sizeof(ip));
                peeraddr.SetAddr(ip);
                peeraddr.SetPort(ntohs(sock_addr.sin6_port));
                peeraddr.SetIsIPV6(true);
            }

            if(message_cb_)
            {
                message_cb_(peeraddr,message_buffer_);
            }
            message_buffer_.retrieveAll();
        }else if(ret < 0)
        {
            if(errno != EINTR && errno != EAGAIN &&  errno != EWOULDBLOCK)
            {
                NEWORK_ERROR << "host: "<< peer_addr_.ToIpPort() <<""<<errno;
                OnClose();
                return;
            }
            break;
        }
   }
   
    
}

void UdpSocket::OnWrite() 
{
    if(closed_)
    {
        NEWORK_WARN << "host:"<<peer_addr_.ToIpPort()<<"had closed";
        return;
    }
   ExtenLife();
    while (true)
    {
        if(!buffer_list_.empty())
        {
            auto buf = buffer_list_.front();
            auto ret = ::sendto(fd_,buf->addr,buf->size,0,buf->sock_addr,buf->sock_len);
            if(ret > 0)
            {
                buffer_list_.pop_front();
            }else if(ret < 0 )
            {
                if(errno != EINTR && errno != EAGAIN &&  errno != EWOULDBLOCK)
                {
                    NEWORK_ERROR << "host: "<< peer_addr_.ToIpPort() <<""<<errno;
                    OnClose();
                    return;
                }
                break;
            }
        }else
        {
            if(write_complete_cb_)
            {
                write_complete_cb_(std::dynamic_pointer_cast<UdpSocket>(shared_from_this()));
            }
            break;
        }
    }
    
}

void UdpSocket::OnClose() 
{
    if(!close_cb_)
    {
        closed_ =  true;
        if(close_cb_)
        {
            close_cb_(std::dynamic_pointer_cast<UdpSocket>(shared_from_this()));
        }
        Event::Close();
    }
}

void UdpSocket::SetCloseCallback(const UdpSocketCloseConnectionCallback &cb)
{
    close_cb_ = cb;
}

void UdpSocket::SetCloseCallback(UdpSocketCloseConnectionCallback &&cb)
{
    close_cb_ = std::move(cb);
}

void UdpSocket::SetRecvMsgCallback(const UdpSocketMessageCallback &cb)
{
    message_cb_ = cb;
}

void UdpSocket::SetRecvMsgCallback(UdpSocketMessageCallback &&cb)
{
    message_cb_ = std::move(cb);
}

void UdpSocket::SetWriteCompleteCallback(const UdpSocketWriteCompleteCallback & cb)
{
    write_complete_cb_ =cb;
}


void UdpSocket::SetWriteCompleteCallback( UdpSocketWriteCompleteCallback &&cb)
{
    write_complete_cb_ =  std::move(cb);
}



void UdpSocket::SetTimeoutCallback(int timeout,const UdpSocketTimeoutCallback &cb)
{
    auto us  = std::dynamic_pointer_cast<UdpSocket>(shared_from_this());
    loop_->RunAfter(timeout,[this,cb,us](){
        cb(us);
    });
}

void UdpSocket:: SetTimeoutCallback(int timeout,UdpSocketTimeoutCallback &&cb)
{
    auto us  = std::dynamic_pointer_cast<UdpSocket>(shared_from_this());
    loop_->RunAfter(timeout,[this,cb,us](){
        cb(us);
    });
}

void  UdpSocket::EnableCheckIdleTimeout(int32_t max_time)
{
    auto tp = std::make_shared<UdpTimeoutEntry>(std::dynamic_pointer_cast<UdpSocket>(shared_from_this()));
    max_idle_time_ = max_time;
    timeout_entry_ = tp;
    loop_->InserEntry(max_time,tp);
}

void UdpSocket::ExtenLife()
{
    auto tp = timeout_entry_.lock();
    if(tp)
    {
        loop_->InserEntry(max_idle_time_,tp);
    }

}

void UdpSocket::Send(std::list<UdpBufferNodePtr>& list)
{
    loop_->RunInLoop([this,&list](){
        SendInLoop(list);
    });
}

void UdpSocket::Send(const char* buf,size_t size,struct sockaddr* addr,socklen_t len)
{
    loop_->RunInLoop([this,buf,size,addr,len](){
        SendInLoop(buf,size,addr,len);
    });
}

void UdpSocket::SendInLoop(std::list<UdpBufferNodePtr>& list)
{
    for(auto &i:list)
    {
        buffer_list_.emplace_back(i);
    }
    if(!buffer_list_.empty())
    {
        EnableWriting(true);
    }
}

void UdpSocket::SendInLoop(const char *buf,size_t size,struct sockaddr* saddr,socklen_t len)
{
    if(buffer_list_.empty())
    {
        auto ret = ::sendto(fd_,buf,size,0,saddr,len);
        if(ret >0 )
        {
            return ;
        }
    }
    auto node = std::make_shared<UdpBufferNode>(buf,size,saddr,len);
    buffer_list_.emplace_back(node);
    EnableWriting(true);
}

void UdpSocket::ForceClose()
{
    loop_->RunInLoop([this] (){
        OnClose();
    });
}